package com.dzm.manager.service.impl;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.utils.uuid.UUID;
import com.ruoyi.common.redis.service.RedisService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import com.dzm.manager.mapper.BrandMapper;
import com.dzm.manager.domain.Brand;
import com.dzm.manager.service.IBrandService;
import org.springframework.util.CollectionUtils;

/**
 * 品牌管理Service业务层处理
 * 
 * @author Dzm
 * @date 2024-09-05
 */
@Service
public class BrandServiceImpl implements IBrandService 
{
    @Autowired
    private BrandMapper brandMapper;

    @Autowired
    private RedisService redisService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;
    /**
     * 查询品牌管理
     * 
     * @param id 品牌管理主键
     * @return 品牌管理
     */
    @Override
    public Brand selectBrandById(Long id)
    {
        return brandMapper.selectBrandById(id);
    }

    /**
     * 查询品牌管理列表
     * 
     * @param brand 品牌管理
     * @return 品牌管理
     */
    @Override
    public List<Brand> selectBrandList(Brand brand)
    {

        return brandMapper.selectBrandList(brand);
    }

    /**
     * 新增品牌管理
     * 
     * @param brand 品牌管理
     * @return 结果
     */
    @Override
    public int insertBrand(Brand brand)
    {
        return brandMapper.insertBrand(brand);
    }

    /**
     * 修改品牌管理
     * 
     * @param brand 品牌管理
     * @return 结果
     */
    @Override
    public int updateBrand(Brand brand)
    {
        return brandMapper.updateBrand(brand);
    }

    /**
     * 批量删除品牌管理
     * 
     * @param ids 需要删除的品牌管理主键
     * @return 结果
     */
    @Override
    public int deleteBrandByIds(Long[] ids)
    {
        return brandMapper.deleteBrandByIds(ids);
    }

    /**
     * 删除品牌管理信息
     * 
     * @param id 品牌管理主键
     * @return 结果
     */
    @Override
    public int deleteBrandById(Long id)
    {
        return brandMapper.deleteBrandById(id);
    }

    /**
     * 直接使用redis实现分布式锁
     * 缺点：不能够确定获取锁后执行操作所需时间，那么就不能够确认锁的过期时间
     *        过期时间过短：操作还没执行完，其他线程就进来了
     *        过期时间过长：如果线程没有删除锁，就突然死亡，导致锁长期存在redis中，其他线程无法获取锁
     * @return
     * @throws InterruptedException
     */
    public List<Brand> findAll2() throws InterruptedException {
        //1.从缓存中获取数据
        List<Brand> brandList = (List<Brand>) redisTemplate.opsForValue().get(CacheConstants.BRAND_LIST);

        //2.缓存中没有数据则取数据库中取数据，并存入缓存
        while (CollectionUtils.isEmpty(brandList)) {
            //为了避免大量消息去访问数据库，通过分布式锁处理
            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
            //2.1 尝试设置一个锁（setIfAbsent是线程互斥的，多个线程操作只有一个线程能设置成功）
            Boolean set = redisTemplate.opsForValue().setIfAbsent("xgb:brand:lock", uuid, 30, TimeUnit.SECONDS);
            if(set) {
                try {
                    // 2.2 判断数据是否仍然为空，仍然为空则查询数据库
                    brandList = (List<Brand>) redisTemplate.opsForValue().get(CacheConstants.BRAND_LIST);
                    if(CollectionUtils.isEmpty(brandList)) {
                        brandList = this.selectBrandList(null);
                        //2.3 数据存入缓存
                        redisTemplate.opsForValue().set(CacheConstants.BRAND_LIST, brandList, 12, TimeUnit.HOURS);

                    }
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    //2.4 释放锁
                    String scriptStr = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
                    DefaultRedisScript<Long> script = new DefaultRedisScript<>(scriptStr);
                    redisTemplate.execute(script, Arrays.asList("lock"), uuid);
                }
            }else {
                //未成功则轮询尝试获取
                Thread.sleep(1000);
            }

        }

        return brandList;
    }

    /**
     * 通过redisson实现分布式锁
     * @return
     * @throws InterruptedException
     */
    public List<Brand> findAll() throws InterruptedException {
        //1.从缓存中获取数据
        List<Brand> brandList = (List<Brand>) redisTemplate.opsForValue().get(CacheConstants.BRAND_LIST);

        //2.缓存中没有数据则取数据库中取数据，并存入缓存
        //2.1 获取一个锁
        RLock lock = redissonClient.getLock("xgb:brand:lock");
        while (CollectionUtils.isEmpty(brandList)) {
            //2.2 尝试锁住（其中有个看门狗的机制，其会进行过期时间的延长，直到操作完成）
            if(lock.tryLock()) {
                //锁成功则执行以下操作
                try {
                    // 2.3 判断数据是否仍然为空，仍然为空则查询数据库
                    brandList = (List<Brand>) redisTemplate.opsForValue().get(CacheConstants.BRAND_LIST);
                    if(CollectionUtils.isEmpty(brandList)) {
                        brandList = this.selectBrandList(null);
                        //2.4 数据存入缓存
                        redisTemplate.opsForValue().set(CacheConstants.BRAND_LIST, brandList, 12, TimeUnit.HOURS);

                    }
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    //2.5 释放锁
                    lock.unlock();
                }
            }else {
                //未成功锁住则轮询尝试
                Thread.sleep(1000);
            }

        }

        return brandList;
    }

}
